package com.devo.icecream.ui.order

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.collection.ArrayMap
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.devo.icecream.AppLogger
import com.devo.icecream.AppToast
import com.devo.icecream.IceCreamApp
import com.devo.icecream.data.*
import com.devo.icecream.exception.APIException
import com.devo.icecream.modbus.PLCManager
import com.devo.icecream.modbus.Recipe
import com.devo.icecream.socket.SocketClient
import com.devo.icecream.ui.BaseActivity
import com.devo.icecream.ui.theme.IcecreamTheme
import com.devo.icecream.utils.bucketIdToPlcId
import com.devo.icecream.utils.plcIdToBucketId
import com.king.zxing.util.CodeUtils
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import java.text.SimpleDateFormat
import java.util.*

private const val defaultCountDown = 120L
private const val finishCountDown = 5L

class OrderActivity : BaseActivity() {

    companion object {
        @JvmStatic
        fun goActivity(
            context: Context,
            goods: ProductBean? = null,
            isDiy: Boolean = false,
            ingredients: List<IngredientsBean>? = null
        ) {
            context.startActivity(
                Intent(context, OrderActivity::class.java).apply {

                    goods?.let {
                        putExtra("goods", it)
                    }

                    ingredients?.let {
                        putParcelableArrayListExtra("ingredients", ArrayList(it))
                    }

                    putExtra("diy", if (isDiy) 1 else 0)
                }
            )
        }
    }

    private val dateFormat by lazy {
        SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA)
    }

    //获取到商品信息
    private val product by lazy {
        intent.getSerializableExtra("goods") as? ProductBean
    }

    //是否是DIY制作
    private val isDiy by lazy {
        intent.getIntExtra("diy", 0)
    }

    //diy的自定义配料表
    private val diyIngredients by lazy {
        try {
            val list = intent.getParcelableArrayListExtra<IngredientsBean>("ingredients")
            list?.toList() ?: listOf()
        } catch (e: Exception) {
            AppLogger.e(e, "获取自定义配料表")
            listOf<IngredientsBean>()
        }
    }

    //订单信息，从服务器获取
    private var order: OrderBean? = null;

    //优惠券编码
    private val couponCode = MutableStateFlow("");

    //优惠券的相关信息
    private val coupon = MutableStateFlow<CouponBean?>(null);

    //温度
    private val temperature = MutableStateFlow(0f)

    //支付ID
    private val payId = MutableStateFlow("");

    //支付类型
    private val payType = MutableStateFlow<Pay>(Pay.WX)

    //广告列表数据
    private val adverts = MutableStateFlow(listOf<Advertising>());

    //订单状态
    private val statusT = MutableStateFlow<OrderStatus>(OrderStatus.None)

    //倒计时
    private val countdown = MutableStateFlow(defaultCountDown)

    //价格
    private val price = MutableStateFlow(-1f)

    private var countDownJob: Job? = null

    //客服电话
    private val servicePhone by lazy {
        IceCreamApp.preferences.getString("service_phone", "************") ?: "************"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            //拿到广告结果并更新界面
            val imageUrls = adverts.collectAsState().value.map { it.picture }

            IcecreamTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {

                    OrderScreen(
                        urls = imageUrls,
                        //collectAsState 根据数据状态更新UI
                        price = price.collectAsState().value,
                        //根据订单状态判断
                        orderStatus = statusT.collectAsState().value,
                        //商品信息
                        product = product,
                        //支付类型 并直接更新UI
                        payType = payType.collectAsState().value,
                        //温度
                        temperature = temperature.collectAsState().value,
                        //diy配料
                        diyIngredients = diyIngredients,
                        //倒计时并更新UI
                        countDown = countdown.collectAsState().value,
                        //优惠券相关信息
                        coupon = coupon.collectAsState().value,
                        //服务手机号
                        servicePhone = servicePhone,
                        onSwitchPayType = {
                            //更新支付类型
                            payType.tryEmit(
                                if (payType.value is Pay.WX) Pay.ALI else Pay.WX
                            )
                            //查询更新本地的订单信息
                            lifecycleScope.launch {
                                //查询订单 确认订单
                                requestOrder()
                            }
                        },
                        onCouponChanged = {
                            it.takeIf { it.isNotBlank() }?.let { str ->
                                //优惠券码
                                couponCode.tryEmit(str.trim())
                                lifecycleScope.launch {
                                    //查询订单
                                    requestOrder()
                                }
                            }
                        },
                        onBack = {
                            finish()
                        },
                    )

                }
            }
        }

        lifecycleScope.launch {
            // 当组件处于 STARTED 或 RESUMED 状态时，这里的代码会被执行
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                withContext(Dispatchers.IO) {
                    onInit()
                }
            }
        }

        startCountdown()
    }

    private suspend fun onInit() {
        coroutineScope {

            launch {
                // 监听支付订单 ID
                SocketClient.getOrderPaidFlow().collectLatest {
                    payId.tryEmit(it.payId)
                }
            }

            launch {
                // 监听订单是否已支付
                payId.collectLatest { id ->
                    order?.let {
                        if (id == it.payId) {
                            statusT.tryEmit(OrderStatus.Paid(statusT.value as OrderStatus.WaitPaid))
                        }
                    }
                }
            }

            launch {
                //collectLatest 函数在新值到达时会自动取消之前的处理操作。如果在处理旧值时有新值到达，旧值的处理操作将被取消。这有助于避免处理过时或无效的值，以确保对最新值的处理。
                statusT.collectLatest { sta ->
                    when (sta) {
                        // 已支付
                        is OrderStatus.Paid -> {
                            //取消倒计时
                            countDownJob?.cancel()
                            order?.let { o ->
                                //检查是否需要补料
                                if (checkStocks(o)) {
                                    //开始制作
                                    doMaking()
                                } else {
                                    //失败，更新制作状态 配料不足错误
                                    statusT.tryEmit(OrderStatus.Fail(MakeError.IngredientsLackError))
                                }
                            } ?: let {
                                AppToast.show("无法开始制作，没有找到订单信息")
                            }

                        }
                        // 失败后发起退款
                        is OrderStatus.Fail -> {
                            AppLogger.e("制作失败：${sta.code}")
                            order?.let {
                                val type =
                                    if (sta.code is MakeError.IngredientsLackError) 3 else 4
                                //调用退款接口
                                refund(it.orderId, type)
                            }
                            //开始倒计时退出
                            startCountdown(finishCountDown)

                        }
                        is OrderStatus.Finish -> {
                            AppLogger.d("制作完成")
                            //检查订单配方
                            checkOrderRecipes()
                            //开始倒计时退出
                            startCountdown(finishCountDown)
                        }
                        // 其他状态设置倒计时
                        else -> {
                            if (countDownJob == null) {
                                startCountdown()
                            }
                        }
                    }
                }
            }

            launch {
                //观察温度变化
                watchTemperature()
            }

            launch {
                //请求广告数据
                requestAdverting()
            }

            launch {
                //查询订单，准备制作 确认订单
                requestOrder()
            }


        }


    }

    private fun startCountdown(seconds: Long = defaultCountDown) {
        //发送倒计时的具体数值
        countdown.tryEmit(seconds)
        countDownJob?.cancel()
        countDownJob = lifecycleScope.launch {
            // 倒计时实现
            launch {
                while (countdown.value > 0) {
                    delay(1_000)
                    //发送倒计时
                    countdown.tryEmit(
                        countdown.value - 1
                    )
                }
            }
            launch {
                //倒计时等于0 结束该界面
                countdown.collectLatest {
                    if (it == 0L && canCountDown) {
                        finish()
                    }
                }
            }
        }

    }

    private var makeProgressJob: Job? = null;

    //开始制作
    private fun startMakeProgress() {
        makeProgressJob?.cancel()
        makeProgressJob = lifecycleScope.launch {
            //制作状态
            var state: OrderStatus.Making?
            val random = Random(System.currentTimeMillis())
            //循环判断执着进度 并更新订单的状态
            while (true) {
                delay(1_000)
                //获取制作进度
                state = statusT.value as? OrderStatus.Making
                if (state != null && state.progress < 0.8f) {
                    //更新订单制作进度
                    statusT.tryEmit(OrderStatus.Making(state.progress + random.nextInt(4) / 100f))
                }
            }
        }

    }

    private suspend fun checkOrderRecipes() {
        order?.let { o ->
            AppLogger.i("开始检查库存")
            val checkAddrMap = o.food.recipes.sortedBy {
                //根据集合中元素的某个属性或值进行排序
                it.sort
            }
                .mapIndexed { index, recipeBean ->//mapIndexed遍历集合中的每个元素，并且对每个元素和它的索引执行一个函数（通过lambda表达式定义），然后将这些函数的结果收集到一个新的集合中。
                    //桶位ID转换为PLC_ID 按照规则将桶位的ID转换成PLC_ID
                    val plcId = bucketIdToPlcId(recipeBean.bucket)
                    val addr = 12000 + index * 2
                    plcId to addr
                }.toMap()

            PLCManager.queryRealRecipe(checkAddrMap) {
                lifecycleScope.launch {
                    //结束订单的时候上报订单号 桶位号ID 以及用量
                    val result = APIClient.finishOrder(
                        o.orderId,
                        it.map { plcIdToBucketId(it.key) to it.value }.toMap()
                    )
                    AppLogger.i("真实用料上报结果：$result");
                }
            }
        }
    }

    //如果订单状态 失败 完成 或者等待的时候 开始倒计时
    private val canCountDown: Boolean
        get() = statusT.value is OrderStatus.WaitPaid
                || statusT.value is OrderStatus.Finish
                || statusT.value is OrderStatus.Fail

    /**
     * 发送 PLC 制作命令
     */
    private suspend fun doMaking() {
        delay(2_000)
        //开始制作 发送制作进度0
        statusT.tryEmit(OrderStatus.Making(0f))
        //开始制作
        startMakeProgress()
        //按后台配置的顺序生成配料表
        val recipe = buildRecipe()

        AppLogger.i("开始制作:${recipe}")
        //调用PLC命令开始制作
        PLCManager.make(
            recipe = recipe,
            listener = object : PLCManager.MakeListener {
                override fun onMake(success: Boolean, code: Int) {
                    if (success) {//制作成功 返回成功状态

                        makeProgressJob?.cancel()
                        makeProgressJob = null
                        //制作中，发送进度1
                        statusT.tryEmit(OrderStatus.Making(1f))
                        //制作状态监控
                        PLCManager.watchCupStatus(
                            object : PLCManager.CupListener {
                                override fun onOutFinish(status: Int) {
                                    when (status) {
                                        //制作完成
                                        2 -> this@OrderActivity.statusT.tryEmit(
                                            OrderStatus.Finish
                                        )
                                        //制作出现错误
                                        3 -> this@OrderActivity.statusT.tryEmit(
                                            OrderStatus.Fail(MakeError.DeviceError)
                                        )
                                    }

                                }
                            }
                        )
                    } else { //制作失败 发送制作失败的错误码
                        statusT.tryEmit(
                            OrderStatus.Fail(MakeError.formInt(code))
                        )
                    }
                }
            }
        )


    }

    private fun checkStocks(order: OrderBean): Boolean {
        //从后台获取的配方中获取相关信息
        return order.food.recipes.all {
            //检查是否需要补料 按照规则将桶位的ID转换成PLC_ID
            !(PLCManager.plcStock?.needSupply(bucketIdToPlcId(it.bucket)) ?: true)
        }
    }

    /**
     * 退款
     * @param orderId String
     * @param type Int 3赠送退款,4失败退款
     */
    private suspend fun refund(orderId: String, type: Int) {
        AppLogger.w("开始退款")
        try {
            APIClient.refundOrder(orderId, type).takeIf { it }?.let {
                AppToast.show("退款成功")
            }
        } catch (e: APIException) {
            AppLogger.e(e, "退款失败")
        }
    }

    /**
     * 监听 PLC 温度
     */
    private suspend fun watchTemperature() {
        while (true) {
            delay(3_000)
            val tempera = PLCManager.plcStatus?.temperature
            temperature.tryEmit(tempera ?: 0f)
        }

    }

    /**
     * 请求 type 为 2 的广告数据
     */
    private suspend fun requestAdverting() {
        try {
            val ads = APIClient.getAdverts(2)
            adverts.tryEmit(ads)
        } catch (e: APIException) {
            AppToast.show(e.message)
        }
    }

    /**
     * 按后台配置的顺序生成配料表
     * @return Recipe
     */
    private fun buildRecipe() = Recipe().apply {
        order?.let { o ->
            //recipes 后台给的配方
            o.food.recipes.sortedBy {
                // 按照后台顺序转换配料
                it.sort
            }.forEach {
                //传入桶位id以及重量
                this.addDosage(bucketIdToPlcId(it.bucket), it.weight)
            }
        }
    }

    /**
     * 请求订单数据，更新倒计时和优惠卷数据
     */
    private suspend fun requestOrder() {
        try {
            //确认订单 请求订单信息
            order = APIClient.requestOrder(
                OrderDto(
                    //优惠券码
                    couponCode = couponCode.value,
                    //从商品信息获取到食品ID
                    foodId = product?.id ?: -1,
                    //是否是DIY制作
                    isDiy = isDiy,
                    //订单支付类型
                    payMode = payType.value.type,

                    ingredients = diyIngredients.let { list ->
                        // 将配方数据转成接口需要的数据
                        list.map { entry ->
                            OrderDto.DIYIngredients(entry.id, if (entry.chose > 1) 1 else 0)
                        }
                    },
                    orderId = order?.orderId ?: ""
                )
            )
        } catch (e: APIException) {
            AppToast.show(e.message)
            return
        }

        order?.let {
            //后台订单获取的实际价格
            price.value = it.actualPrice
            //这是什么过期时间？待确认？
            dateFormat.parse(it.timeExpire)?.let { d ->
                countdown.emit(
                    (d.time - System.currentTimeMillis()) / 1000
                )
            }
            //优惠券
            it.coupon?.let { c ->
                coupon.tryEmit(c)
            }
            //更新订单状态
            statusT.tryEmit(
                //创建订单价格以及二维码
                OrderStatus.WaitPaid(
                    price = it.actualPrice,
                    imgUrl = CodeUtils.createQRCode(it.qrCodeUrl, 300).asImageBitmap(),
                )
            )
        }

    }

}

sealed class OrderStatus {

    object None : OrderStatus()

    //等待付费
    open class WaitPaid(
        val price: Float,
        val imgUrl: ImageBitmap? = null,
    ) : OrderStatus();
    //
    open class Paid(val paid: WaitPaid) : OrderStatus()

    //制作进度
    open class Making(val progress: Float) : OrderStatus()

    //制作完成
    object Finish : OrderStatus()

    /**
     *
     * @property code 0等待中，1正常完成，2料不足退款，3故障维修
     */
    class Fail(val code: MakeError) : OrderStatus()
}

sealed class MakeError {
    object None : MakeError()
    object IngredientsLackError : MakeError()
    object DeviceError : MakeError()

    companion object {
        fun formInt(code: Int): MakeError {
            return if (code == 2) IngredientsLackError
            else DeviceError
        }
    }
}

fun main() {
    val map = ArrayMap<Int, Float>()
    (1..9).forEach {
        map[it] = "0.${it}f".toFloat()
    }

    println(map)
}